// Copyright Abhijeet Nayak @2013
// Please contact abhijeet@cise.ufl.edu before copying the code
 
#include "TwoWayList.h"
#include "Record.h"
#include "Schema.h"
#include "File.h"
#include "Comparison.h"
#include "ComparisonEngine.h"
#include "DBFile.h"
#include "Defs.h"
#include <iostream>
#include <fstream>
#include <string.h>

extern "C" {
	int yyparse(void);   // defined in y.tab.c
}

DBFile::DBFile () {
	NoOfPages=0;
	CurrentPage=0;
	f_path=NULL;
}

//Create the binary db file to store file class
int DBFile::Create (char *fpath, fType f_type, void *startup) {
	//Save the file path
	f_path=strdup(fpath);
	file.Open(0,f_path);
	file.Close();
}

//Save the data from the text file to binary db file
void DBFile::Load (Schema &f_schema, char *loadpath) {
	
	//Count the no of pages
	off_t pageCount=0;
	
	//Check if Create is called and DB file is created before calling Load
	if( f_path != NULL ){
		
		//Open the binary file database to which data will be dumped
		file.Open(1,f_path);
		
		//Open the text database from which data will be fetched
		FILE *TextFile = fopen(loadpath,"r");
		
		//Clean the page before further processing
		page.EmptyItOut();
		
		//If text file opened successfully, start processing it
		if ( TextFile != NULL ){
			
			//Read all the records from the text file
			while (record.SuckNextRecord(&f_schema, TextFile)){
								
				//Write the records to the page
				if ( !page.Append(&record) )
				{
					//Come here if page is full
					//add the full page to file and empty it's content
					file.AddPage(&page,pageCount);
					pageCount++;
					page.EmptyItOut();
					page.Append(&record);
				}
			}
			//Check if the page is empty, if not flush its content to DB
			if ( !page.IsEmpty() ){
				file.AddPage(&page,pageCount);
				page.EmptyItOut();
			}
			cout << " pages written to disk : " << pageCount << endl;
		}
		else{ //file failed to open
			cout << "filed to open the file" << endl;
		}
		//Finaly close file
		Close();
	}
	else{ //else of error for calling load without create
		cout << "First call create and then call Load to load the DB" << endl;
		//exit(1);
	}
}

//Open the binary db file for reading
int DBFile::Open (char *f_path) {
	//First check if the f_path is valid
	ifstream infile;
	infile.open (f_path,ifstream::in);

	if (!infile.good()){
		cout << "File "<< f_path << "does not exist" << endl;
		//Return failure
		return 1;
	}else{
		//Close the stream
		infile.close();
	}
	//Open the DB file
	file.Open(1, f_path);
	
	//Return Success
	return 0;
}

void DBFile::Add (Record &rec) {
	//Get total no of pages in the file
	off_t LastPage;
	int pages = file.GetLength();
	
	//GetLength will retun 0 if the db is empty
	//And return 2 if there is only 1 page and so on
	if ( pages >= 2 ) {
		LastPage=pages-2;
	}
	
	//If there is atleast one page in the file
	if ( pages >= 2 ){
		//Fetch the last page
		file.GetPage(&page, LastPage);
		
		//Add the record to the page and if it failes then create new page
		if ( !page.Append(&rec) ){
			page.EmptyItOut();
			page.Append(&rec);
			LastPage++;
		}
	}else{ //If the file is empty
		page.Append(&rec);
		LastPage=0;
	}
	//Finally write the page to file
	file.AddPage(&page,LastPage);
}

//Close the binary db file
int DBFile::Close () {
	file.Close();
}

void DBFile::MoveFirst () {
	//Cleanup the page 
	page.EmptyItOut();
	//Load the page with first page data
	file.GetPage(&page,0);
	//Set pagecounter
	CurrentPage=0;
}


//Fetch records from the binary db file, 
//after it is open by Open function
int DBFile::GetNext (Record &fetchme) {
	//Total no of pages in the db
	NoOfPages=file.GetLength()-1;
	
	//Incase of first fetch	
	if ( CurrentPage == 0 ){
		file.GetPage(&page,CurrentPage);
		CurrentPage++;
		GetNext(fetchme);
	}else{
		//Get record from page or go to else
		if(page.GetFirst(&fetchme)){
			return 1;
		}else{
			//Check if there are more pages left in the file
			//Check if we are under 
			if( CurrentPage < NoOfPages ){
				file.GetPage(&page,CurrentPage);
				CurrentPage++;
				GetNext(fetchme);
				
			}else{
				return 0;
			}
		}
	}
}

int DBFile::GetNext (Record &fetchme, CNF &cnf, Record &literal) {
	
	Record temp;
	//Total no of pages in the db
	NoOfPages=file.GetLength()-1;
	
	//Check if first page if fetch, if not fetch first page
	if( CurrentPage == 0 ){
		file.GetPage(&page,CurrentPage);
		CurrentPage++;
		return (GetNext(fetchme, cnf, literal));
	}else{
		//Continue processing till there are more pages in the file
		while( CurrentPage <= NoOfPages ){
			//Fetch the record from class
			if(page.GetFirst(&temp)){
				int cond = comp.Compare (&temp, &literal, &cnf);
				if(cond){
					fetchme.Consume(&temp);
					return 1;
				}
				else{
					continue;
				}
			}else{
				if( CurrentPage < NoOfPages ){
					file.GetPage(&page,CurrentPage);
					CurrentPage++;
					return(GetNext(fetchme, cnf, literal));
				}else{
					return 0;
				}
			}
		}
	}	
}
